# Python GPIO development ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # python-periphery introduction **python-periphery** is a library for hardware peripheral (GPIO, SPI, I2C, etc.) development on Linux systems through Python. ## Main features - **Based on Linux kernel interface**: Uses standard Linux character device interfaces (such as `/dev/gpiochip0`) - **Cross-platform compatibility**: Suitable for various Linux single-board computers (Raspberry Pi, BeagleBone, Radxa, etc.) - **Pure Python implementation**: No need to compile C extensions, easy to install and deploy - **Supports multiple peripherals**: GPIO, SPI, I2C, MMIO, Serial, PWM, etc. ## Supported peripheral types | **Peripheral type** | **Description** | | --- | --- | | GPIO | General purpose input/output | | SPI | Serial peripheral interface | | I2C | I²C bus communication | | MMIO | Memory-mapped I/O | | Serial | Serial port communication | | PWM | Pulse width modulation | # Install python-periphery ## Install pip3 First, ensure pip3 is installed in the system: ```bash sudo apt update sudo apt install python3-pip -y ``` ## Install python-periphery library Due to PEP 668 protection mechanism in Python 3.13+, you need to use the `--break-system-packages` parameter: ```bash pip3 install python-periphery --break-system-packages ``` Verify installation: ```bash python3 -c "from periphery import GPIO; print('Installation successful!')" ``` **Tip**: For embedded development boards, using the `--break-system-packages` parameter is safe. If you prefer to use a virtual environment, you can refer to the [Python Virtual Environment Usage](<../Python Virtual Environment Usage/Python Virtual Environment Usage.md>) documentation. # GPIO reading > **Important note**: Pin3 and Pin5 are configured as I2C9 interface (SDA and SCL) by default. If you need to use them as GPIO, please ensure these pins are not occupied by any other devices. ## Hardware preparation - Main board - Jumper wires ## Software preparation ### Test code The following codes use the python-periphery library to read the high and low levels of Pin3 (GPIO36). Create file `gpio_input.py`: ```python from periphery import GPIO import time def read_gpio_input():# Configure GPIO input (modify pin number according to actual hardware)# Pin3 corresponds to GPIO36 (pin 36 of /dev/gpiochip4)try:# Initialize GPIO in input mode gpio_in = GPIO("/dev/gpiochip4", 36, "in")print("Starting GPIO input reading (press Ctrl+C to exit)")while True:# Read pin value value = gpio_in.read()print(f"GPIO input value: {value} (True=High, False=Low)") time.sleep(1) # Read once per secondexcept KeyboardInterrupt:print("\nProgram exited")except Exception as e:print(f"Error occurred: {e}")finally:# Ensure resources are releasedtry: gpio_in.close()except:passif __name__ == "__main__": read_gpio_input() ``` ## Test steps 1. Connect Pin3 (GPIO36) to GND or 3.3V pin 2. Save the code as `gpio_input.py` 3. Run the test code with the following command: ```bash python3 gpio_input.py ``` ## Experiment results The terminal will output `False` or `True` information: - `False` represents low level (pin connected to GND) - `True` represents high level (pin connected to 3.3V) Output example: ```plaintext Starting GPIO input reading (press Ctrl+C to exit) GPIO input value: False (True=High, False=Low) GPIO input value: False (True=High, False=Low) GPIO input value: True (True=High, False=Low) ``` # GPIO output ## Hardware preparation - Main board - Jumper wires ## Software preparation ### Test code The following code uses the python-periphery library to control Pin3 (GPIO36) to output high and low levels, then reads Pin3's high and low levels through Pin5 (GPIO37). Create file `gpio_output.py`: ```python from periphery import GPIO import time def gpio_output_with_feedback():# GPIO Configuration (modify pin numbers based on your hardware)# Pin3/GPIO36 (output) → maps to pin 36 of /dev/gpiochip4# Pin5/GPIO37 (input) → maps to pin 37 of /dev/gpiochip4 OUTPUT_PIN_CHIP = "/dev/gpiochip4" OUTPUT_PIN_NUMBER = 36 # Pin3/GPIO36 (output pin, controlled by the script) INPUT_PIN_NUMBER = 37 # Pin5/GPIO37 (input pin, reads output state)# Initialize GPIO objects as None first (for safe release later) gpio_out = None gpio_in = Nonetry:# Initialize Pin3/GPIO36 as OUTPUT mode gpio_out = GPIO(OUTPUT_PIN_CHIP, OUTPUT_PIN_NUMBER, "out")# Initialize Pin5/GPIO37 as INPUT mode gpio_in = GPIO(OUTPUT_PIN_CHIP, INPUT_PIN_NUMBER, "in")# Print test initialization infoprint("=== GPIO Output-Input Feedback Test Started ===")print(f"Controlled Pin (GPIO36): {OUTPUT_PIN_CHIP} - Pin {OUTPUT_PIN_NUMBER} (OUTPUT)")print(f"Monitoring Pin (GPIO37): {OUTPUT_PIN_CHIP} - Pin {INPUT_PIN_NUMBER} (INPUT)")print("Test Behavior: GPIO36 toggles HIGH/LOW every 1s; GPIO37 verifies GPIO36's state")print("Press Ctrl+C to stop the test\n")# Main loop: Toggle GPIO36 and read GPIO37 feedbackwhile True:# 1. Set GPIO36 to HIGH level gpio_out.write(True) time.sleep(0.1) # Short delay for signal stabilization (avoid read lag) gpio37_reading = gpio_in.read()print(f"GPIO36 Output: HIGH (True) | GPIO37 Reading: {gpio37_reading}")# Keep GPIO36 HIGH for 1 second time.sleep(1)# 2. Set GPIO36 to LOW level gpio_out.write(False) time.sleep(0.1) # Short delay for signal stabilization gpio37_reading = gpio_in.read()print(f"GPIO36 Output: LOW (False) | GPIO37 Reading: {gpio37_reading}")# Keep GPIO36 LOW for 1 second time.sleep(1)# Handle user-initiated exit (Ctrl+C)except KeyboardInterrupt:print("\n\nTest stopped by user (Ctrl+C)")# Handle other unexpected errors (e.g., GPIO access failure)except Exception as e:print(f"\nError during test: {str(e)}")# Ensure GPIO resources are released even if an error occursfinally:print("\nReleasing GPIO resources...")# Safely close GPIO36 (set to LOW first to avoid residual high level)if gpio_out:try: gpio_out.write(False) gpio_out.close()print(f"Successfully closed GPIO36 (Pin {OUTPUT_PIN_NUMBER})")except Exception as close_err:print(f"Failed to close GPIO36 (Pin {OUTPUT_PIN_NUMBER}): {str(close_err)}")# Safely close GPIO37if gpio_in:try: gpio_in.close()print(f"Successfully closed GPIO37 (Pin {INPUT_PIN_NUMBER})")except Exception as close_err:print(f"Failed to close GPIO37 (Pin {INPUT_PIN_NUMBER}): {str(close_err)}")print("Resource release complete.")# Run the test when the script is executed directlyif __name__ == "__main__": gpio_output_with_feedback() ``` ## Test steps 1. Short Pin3 (GPIO36) and Pin5 (GPIO37) 2. Save the code as `gpio_output.py` 3. Run the test code with the following command: ```bash sudo python3 gpio_output.py ``` > **Note**: GPIO output may require root permissions, so use `sudo` to run. ## Experiment results The terminal will output `False` or `True` information: - `False` represents low level - `True` represents high level Output example: ```plaintext === GPIO Output-Input Feedback Test Started === Controlled Pin (GPIO36): /dev/gpiochip4 - Pin 36 (OUTPUT) Monitoring Pin (GPIO37): /dev/gpiochip4 - Pin 37 (INPUT) Test Behavior: GPIO36 toggles HIGH/LOW every 1s; GPIO37 verifies GPIO36's state Press Ctrl+C to stop the test GPIO36 Output: HIGH (True) | GPIO37 Reading: True GPIO36 Output: LOW (False) | GPIO37 Reading: False GPIO36 Output: HIGH (True) | GPIO37 Reading: True GPIO36 Output: LOW (False) | GPIO37 Reading: False ``` # GPIO pin mapping description In python-periphery, you need to specify two parameters for GPIO pins: 1. **GPIO chip device**: Quectel Pi H1 primarily uses `/dev/gpiochip4` 2. **Pin number**: GPIO number corresponding to the physical pin ## 40Pin mapping example | **Physical Pin ID** | **Pin name** | **GPIO number** | **GPIO chip** | **Pin number** | **Default function** | | --- | --- | --- | --- | --- | --- | | Pin3 | NFC_I2C_SDA | GPIO36 | /dev/gpiochip4 | 36 | I2C09_SDA | | Pin5 | NFC_I2C_SCL | GPIO37 | /dev/gpiochip4 | 37 | I2C09_SCL | | Pin7 | CAM2_RST | GPIO77 | /dev/gpiochip4 | 77 | GPIO | | Pin11 | GPIO_16 | GPIO16 | /dev/gpiochip4 | 16 | GPIO/SPI/UART/I2C | | Pin13 | GPIO_17 | GPIO17 | /dev/gpiochip4 | 17 | GPIO/SPI/UART/I2C | ## View available GPIO Use the `gpioinfo` command to view all available GPIO chips and pins: ```bash # Install gpiod toolssudo apt install gpiod -y # View all GPIO information gpioinfo # View a specific GPIO chip gpioinfo /dev/gpiochip4 ``` # Common issues ## Permission issues If you encounter permission errors, you can: 1. Run the script with `sudo` 2. Add user to the `gpio` user group: ```bash sudo usermod -a -G gpio $USER ``` 3. Log out and log back in for permissions to take effect ## Pin occupancy issue If you encounter `[Errno 16] Device or resource busy` or `[Errno 1] Operation not permitted` errors: **Check pin state:** ```bash # View all GPIO pins state gpioinfo | grep "consumer="# View pin occupation for a specific chip gpioinfo /dev/gpiochip4 | grep -E "(line|consumer)" ``` **Common causes:** - Pin is occupied by other processes (such as I2C, SPI, etc.) - Pin is already used by system functions - Other GPIO programs are running **Solutions:** - Select unoccupied pins (pins shown as `input` with no `consumer` in gpioinfo) - If you need to use the occupied pins, you need to disable the corresponding functions ## Pin number error If you encounter `[Errno 22] Invalid argument` error: **Causes:** - Pin number does not exist or is unavailable - Wrong GPIO chip device is used **Solutions:** 1. Use `gpioinfo` to view actual available pin numbers 2. Make sure to use the correct GPIO chip (Quectel Pi H1 primarily uses `/dev/gpiochip4`) ## Debugging tips **View available GPIO chips:** ```bash ls -l /dev/gpiochip* ``` **View detailed information of GPIO chip:** ```bash gpioinfo ``` **Test specific pin:** ```bash # Use gpioget to read pin valuesudo gpioget -c gpiochip4 36# Use gpioset to set pin valuesudo gpioset -c gpiochip4 36=1 # Set to high levelsudo gpioset -c gpiochip4 36=0 # Set to low level ``` # Reference resources - [python-periphery GitHub repository]() - [python-periphery official documentation]() - [40-Pin expansion](<../../Usage guide/40-Pin expansion/40-Pin expansion.md>) - [Python Virtual Environment Usage](<../Python Virtual Environment Usage/Python Virtual Environment Usage.md>) - [C/C++ GPIO development](<../C_C++ GPIO development/C_C++ GPIO development.md>)